/* file: parser.y */

/* 使用指令％skeleton "lalr1.cc"选择C++解析器的骨架 */
%skeleton "lalr1.cc"

/* 指定bison的版本 */
%require "3.2"
%define api.namespace {Marker} //声明命名空间与下面声明的类名结合使用 Marker::Parser::  在scanner.l中有体现
%define api.parser.class {Parser}
%define api.token.constructor
%define api.value.type variant //使得类型与token定义可以使用各种复杂的结构与类型
%define parse.assert  //开启断言功能
%defines  //生成各种头文件  location.hh position.hh  parser.hpp
%code requires
{
  /*requires中的内容会放在YYLTYPE与YYSTPYPE定义前*/
  #include <iostream>
  #include <string>
  #include <vector>
  #include <stdint.h>
  #include <cmath>
  #include "ast.h"

  using namespace std;

  namespace Marker { /*避免包含头文件时冲突*/
    class Scanner;
    class Driver;
  }
}

%code top
{
  /*尽可能放在parser.cpp靠近头部的地方，与requires相似*/
  #include <iostream>
  #include "scanner.h"
  #include "parser.hpp"
  #include "driver.h"
  #include "location.hh"

  /*注意：这里的参数由%parse-param决定*/
  static Marker::Parser::symbol_type yylex(Marker::Scanner& scanner,Marker::Driver &driver){
    return scanner.nextToken();
  }
  using namespace Marker;
}

/*定义parser传给scanner的参数*/
%lex-param { Marker::Scanner& scanner }
%lex-param { Marker::Driver& driver }

/*定义driver传给parser的参数*/
%parse-param { Marker::Scanner& scanner }
%parse-param { Marker::Driver& driver }

%locations
//%define parse-trace

/*详细显示错误信息*/
%define parse.error verbose

/*通过Marker::Parser::make_XXX(loc)给token添加前缀*/
%define api.token.prefix {TOKEN_}

%token <int> ADD SUB MUL DIV EQL ABS
%token COM SEM LP RP LB RB
%token <int> CMP
%token <int> LOR LAND
%token IF FI THEN ELSE
%token WHILE DO DONE LET 
%token <Fncall::bifs> FUNC
%token NAME
%token <std::shared_ptr<Numval>> NUMBER
%token EOL END

%right EQL
%left LOR
%left LAND
%nonassoc CMP
%left ADD SUB
%left MUL DIV
%left ABS UMINUS
%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE

%start calclist

%type <std::shared_ptr<Statement>> stmt
%type <std::shared_ptr<Expression>> exp
%type <std::shared_ptr<Symbol>> NAME
%type <std::shared_ptr<StatementList>> stmtlist
%type <std::shared_ptr<ExpressionList>> explist
%type <std::shared_ptr<SymbolList>> symlist
%%
stmt: IF exp LB stmtlist RB %prec LOWER_THAN_ELSE {
      $$ = std::make_shared<Statement>(std::make_shared<Flow>(Flow::flowType::IF, $2, $4, Ast::nullAst));
    }
    | IF exp LB stmtlist RB ELSE LB stmtlist RB {
      $$ = std::make_shared<Statement>(std::make_shared<Flow>(Flow::flowType::IF, $2, $4, $8));
    }
    | WHILE exp LB stmtlist RB {
      $$ = std::make_shared<Statement>(std::make_shared<Flow>(Flow::flowType::WHILE, $2, $4, Ast::nullAst));
    }
    | exp SEM {
      $$ = std::make_shared<Statement>($1);
    }
    ;

stmtlist: stmt  {
      $$ = std::make_shared<StatementList>($1);
    }
    | stmtlist stmt {
      $$ = $1 + $2;
    }
    ;

exp: exp CMP exp {$$ = std::make_shared<Expression>($2, $1, $3);}
    | exp ADD exp {$$ = std::make_shared<Expression>($2, $1, $3);}
    | exp SUB exp {$$ = std::make_shared<Expression>($2, $1, $3);}
    | exp MUL exp {$$ = std::make_shared<Expression>($2, $1, $3);}
    | exp DIV exp {$$ = std::make_shared<Expression>($2, $1, $3);}
    | exp LOR exp {$$ = std::make_shared<Expression>($2, $1, $3);}
    | exp LAND exp {$$ = std::make_shared<Expression>($2, $1, $3);}
    | ABS exp {$$ = std::make_shared<Expression>($1, $2, Ast::nullAst);}
    | LP exp RP {$$ = $2;}
    | SUB exp %prec UMINUS {$$ = std::make_shared<Expression>(token::TOKEN_UMINUS, $2, Ast::nullAst);}
    | NUMBER {$$ = std::make_shared<Expression>(0, $1, Ast::nullAst);}
    | NAME {$$ = std::make_shared<Expression>(0, $1, Ast::nullAst);}
    | NAME EQL exp {$$ = std::make_shared<Expression>($2, $1, $3);}
    | FUNC LP explist RP {$$ = std::make_shared<Expression>(0, std::make_shared<Fncall>($1, $3), Ast::nullAst);}
    | NAME LP explist RP {$$ = std::make_shared<Expression>(0, std::make_shared<Ufncall>($1, $3), Ast::nullAst);}
    ;

explist: exp  {
      $$ = std::make_shared<ExpressionList>($1);
    }
    | explist COM exp {
      $$ = $1 + $3;
    }

symlist: NAME {
      $$= std::make_shared<SymbolList>($1);
    }
    | symlist COM NAME {
      $$ = $1 + $3;
    }
    ;

calclist:
    | calclist stmtlist EOL {
                          if (Ast::dump) {
                            Ast::dumpAst($2);
                          }
                          std::cout << " = " << Ast::eval($2) << std::endl;
                          std::cout << "> ";
                        }
    | calclist LET NAME LP symlist RP EQL stmtlist EOL {
      std::cout << $5 << std::endl;
      $3->param = $5;
      $3->func = $8;
      std::cout << "Defined "  << $3->name << "\n> ";
    }
    | calclist error EOL {yyerrok; std::cout << "> ";}
    | calclist EOL {std::cout << "> ";}
    | calclist END {return 0;}
    ;

%%
/*Parser实现错误处理接口*/
void Marker::Parser::error(const Marker::location& location,const std::string& message){
  std::cout<<"msg:"<<message
           <<", error happened at: "<<location<<std::endl;
}